<!-- 截图框相关组件 -->
<template>
  <div
    id="clip-rect-page"
    ref="rectBox"
    v-loading="showLoading"
    v-draggable:[dragControl]="dragBack"
    class="rect-page"
    :style="rectStyle"
  >
    <canvas
      v-if="curPen > 0"
      id="paint-canvas"
      :class="['rect-box', curPen > 0 ? 'pen-cursor': '']"
    />
    <el-popover
      width="150"
      popper-class="popper-pen"
      trigger="hover"
    >
      <div
        class="pen-box"
      >
        <div
          v-for="item in penList"
          :key="item.id"
          :class="['pen-item', 'pen-size-' + item.id, {'active': item.id === curPen}]"
          @click="penChoose(item.id)"
        />
      </div>
      <span
        v-show="bottomControl && !showLoading"
        slot="reference"
        :class="['out-item', {'out-active': curPen !== -1}]"
        @click="penChoose(14)"
      >
        画笔
      </span>
    </el-popover>
  </div>
</template>
<script>
import draggable from '@/directives/draggable';
import {
  CLIPSTATUS,
} from '../data/constant';
import {
  changeDpiBlob
} from 'changedpi';
export default {
  directives: {
    draggable,
  },
  props: {
    /**
     * 页面比例参数 
     */
    pageImage: {
      type: Object,
      default: () => {},
    },
    /**
     * 截图坐标详情（xywh数据）
     */
    detail: {
      type: Object,
      default: () => null,
    },
    /**
     * 当前图片路径
     */
    src: {
      type: String,
      default: '',
    },
    /**
     * 截图状态
     */
    clipStatus: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      showLoading: false, // 截图遮罩
      canvasHistory: [], // 涂鸦历史数组
      draggable: false, // 当前矩形框是否在拖动
      rect: null, // 矩形dom
      canvas: null,
      context: null,
      curPen: -1, // 当前画笔尺寸
      penList: [ // 画笔尺寸集合
        {
          id: 10,
          name: '小',
        },
        {
          id: 14,
          name: '中',
        },
        {
          id: 18,
          name: '大',
        },
      ],
    };
  },
  computed: {
    /**
     * 外层矩形框样式
     */
    rectStyle() {
      return {
        boxShadow: '0 0 0 1999px rgba(0, 0, 0, .4)',
        width: this.detail.width * this.pageImage.imgScale + 'px',
        height: this.detail.height * this.pageImage.imgScale + 'px',
        left: this.detail.startX * this.pageImage.imgScale + 'px',
        top: this.detail.startY * this.pageImage.imgScale + 'px',
        borderColor: '#8400FF',
        cursor: this.clipStatus ? 'crosshair' : 'move',
      };
    },
    /**
     * 拖动控制
     */
    dragControl() {
      return this.clipStatus === CLIPSTATUS.FINISH && this.curPen === -1;
    },
    /**
     * 控制显示当前矩形框底部功能栏的展示
     */
    bottomControl() {
      return this.clipStatus === CLIPSTATUS.FINISH && !this.draggable;
    },
  },
  watch: {
    /**
     * 只有选中了画笔的具体值 才能进行绘制
     * @param {*} val
     */
    curPen(val) {
      if (this.canvas) return;
      if (val > 0) {
        this.$nextTick(() => {
          this.initCanvas();
          this.initRect();
        });
      } else {
        this.clearRect();
      }
    },
    /**
     * 截图状态
     * @param {*} val 0 => 否， 1 => 是（已就绪）,2 => 是（正在截图）, 3 => 是（完成截图）
     */
    clipStatus(val) {
      if (val === 3 && !this.draggable) { // 完成截图且处于静止状态时 canvas生成图片文件
        this.getClip().then(canvas => {
          this.showLoading = false;
          canvas.toBlob(async(blob) => {
            this.$emit('paintFinish', await this.blob2file(blob));
            this.clearAll();
          }, 'image/jpeg', 1);
        }, () => {
          this.showLoading = false;
        });
      }
    },
  },
  methods: {
    /**
     * 自定义指令拖动回调 用于区分当前是拖动情况还是点击情况
     * @param {*} e
     */
    dragBack(e) {
      this.draggable = e;
      this.$nextTick(() => {
        this.rect = this.$refs.rectBox;
        const left = Number(this.rect.style.left.split('px')[0]);
        const top = Number(this.rect.style.top.split('px')[0]);
        const width = Number(this.rect.style.width.split('px')[0]);
        const height = Number(this.rect.style.height.split('px')[0]);
        const childDetail = {
          startX: left / this.pageImage.imgScale, // 拖动时，将坐标转换成实际比例反注入到detail数据里
          startY: top / this.pageImage.imgScale,
          width: width / this.pageImage.imgScale, // 拖动时，将坐标转换成实际比例反注入到detail数据里
          height: height / this.pageImage.imgScale
        };
        this.$emit('update:detail', childDetail);
        if (!e && this.detail.width > 2 && this.detail.height > 2) { // 当处于静止状态时再截图
          this.getClip().then(canvas => {
            this.showLoading = false;
            canvas.toBlob(async(blob) => {
              this.$emit('paintFinish', await this.blob2file(blob));
              this.clearAll();
            }, 'image/jpeg', 1);
          }, () => {
            this.showLoading = false;
          });
        }
      });
    },
    /**
     * 画笔大小控制
     */
    penChoose(size = 14) {
      this.curPen = size;
    },
    /**
     * 初始化canvas
     */
    initCanvas() {
      this.canvas = document.getElementById('paint-canvas');
      this.canvas.width = this.detail.width * this.pageImage.imgScale;
      this.canvas.height = this.detail.height * this.pageImage.imgScale;
      this.context = this.canvas.getContext('2d');
      this.context.lineJoin = 'round';
    },
    /**
     * 画笔涂鸦矩形框操作
     */
    initRect() {
      let start = {};
      this.canvasHistory = [];
      this.canvas.onmousedown = e => {
        start = {
          x: e.offsetX,
          y: e.offsetY,
        };
        this.context.lineWidth = this.curPen;
        this.context.strokeStyle = '#fff';
        this.context.beginPath();
        this.context.moveTo(start.x, start.y);
        this.canvasHistory.push({
          type: 'start',
          x: e.offsetX / this.pageImage.imgScale,
          y: e.offsetY / this.pageImage.imgScale,
        });
        this.canvas.onmousemove = e => {
          this.context.lineTo(e.offsetX, e.offsetY);
          this.context.stroke();
          this.canvasHistory.push({
            type: 'ing',
            x: e.offsetX / this.pageImage.imgScale,
            y: e.offsetY / this.pageImage.imgScale,
          });
        };
        this.canvas.onmouseup = () => {
          this.canvasHistory.push({
            type: 'end',
          });
          this.context.closePath();
          this.canvas.onmousemove = null;
          document.oncontextmenu = null;
          this.getClip(false).then(canvas => {
            return this.generateClip(canvas);
          }, () => {
            this.showLoading = false;
          }).then(canvas => {
            canvas.toBlob(async(blob) => {
              this.$emit('paintFinish', await this.blob2file(blob));
            }, 'image/jpeg', 1);
          });
        };
      };
    },
    /**
     * 生成涂鸦来对最后截图进行修改
     */
    generateClip(canvas) {
      const context = canvas.getContext('2d');
      // eslint-disable-next-lint
      for (const item of this.canvasHistory) {
        if (item.type === 'start') {
          context.lineWidth = this.curPen / this.pageImage.imgScale;
          context.strokeStyle = '#fff';
          context.beginPath();
          context.moveTo(item.x, item.y);
        } else if (item.type === 'ing') {
          context.lineTo(item.x, item.y);
          context.stroke();
        } else {
          context.closePath();
          break; // 有涂鸦记录就先将涂鸦绘制到canvas上再返回canvas
        }
      }
      return canvas; // 没有涂鸦记录直接返回canvas
    },
    /**
     * 清除全部
     */
    clearAll() {
      this.clearCanvas();
      this.clearRect();
    },
    /**
     * 清除画笔及涂鸦记录
     */
    clearRect() {
      this.curPen = -1;
      this.canvasHistory = [];
    },
    /**
     * 清除canvas
     */
    clearCanvas() {
      this.context && this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    },
    /**
     * 获取裁剪区域图像
     */
    getClip(type = true) {
      return new Promise((resolve, reject) => {
        if (type) {
          this.showLoading = true;
        }
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');
        const image = new Image();
        image.crossOrigin = 'Anonymous';
        const dpr = 2;
        image.onload = () => {
          // 宽高及上下偏移都是基于原始比例进行裁剪，但目前发现超过5行截图会高度缺失！
          const scale = this.pageImage.scale / 100;
          const imgX = this.detail.startX / scale;
          const imgY = this.detail.startY / scale;
          const imgW = this.detail.width / scale;
          const imgH = this.detail.height / scale;
          canvas.width = imgW;
          canvas.height = imgH;
          canvas.style.width = imgW / dpr + 'px';
          canvas.style.height = imgH / dpr + 'px';
          context.drawImage(
            image, // 规定要使用的图像、画布或视频。
            imgX, imgY, // 开始剪切的 x, y坐标位置。
            imgW, imgH, // 被剪切图像的宽高。 
            0, 0, // 在画布上放置图像的 x 、y坐标位置。
            canvas.width, canvas.height, // 要使用的图像的宽度、高度
          );
          context.scale(0.5, 0.5);
          setTimeout(() => {
            resolve(canvas);
          });
        };
        image.src = `${this.src}?t=${Date.now()}`;
        image.onerror = () => {
          reject('图片错误');
        };
      });
    },
    /**
     * 随机id
     */
    uuid() {
      let d = new Date().getTime();
      const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
      });
      return uuid;
    },
    /**
     * canvas转base64
     * @param {*} blob
     * @param {*} type
     * @param {*} name
     */
    async blob2file(blob, type = 'jpg', name = '') {
      const fileObj = {
        file: null,
        base64: null
      };
      const res = await changeDpiBlob(blob, this.pageImage.origin.xDpi).then(async newBlob => {
        const fileName = name || this.uuid() + '.' + type;
        fileObj.file = new File([newBlob], fileName, { type: newBlob.type, lastModified: Date.now() });
        return fileObj;
      });
      const finalRes = await new Promise((resolve) => {
        const reader = new FileReader();
        reader.readAsDataURL(res.file);
        reader.onload = function (e) {
          resolve(e.target.result);
        };
      }).then(base64 => {
        res.base64 = base64;
        return res;
      });
      return finalRes;
    }
  },
};
</script>
<style lang="scss" scoped>
.rect-page{
  position: absolute;
  left: -9999px;
  top: 0;
  border: 1px solid #8400ff;
  background-color: rgba(255, 255, 255, 0.5);
  cursor: no-drop;
  z-index: 8;
  .rect-box {
    width: 100%;
    height: 100%;
  }
  .pen-cursor{
    cursor: url() 6 28, auto;
  }
  .out-item{
    background-color: rgba(0, 0, 0, .5);
    text-align: center;
    padding: 3px 5px;
    pointer-events: auto;
    cursor: pointer;
    position: absolute;
    bottom: -27px;
    left: 0;
    user-select: none;
    white-space: nowrap;
  }
  .out-active{
    color: #f59a23;
  }
}
.resize{
  &::after{
    content: '';
    position: absolute;
    border-radius: 50%;
    width: 8px;
    height: 8px;
    border: 2px solid #8400ff;
    background-color: #fff;
    right: -4px;
    bottom: -4px;
    cursor: nwse-resize;
    z-index: 2;
  }
}
</style>
<style lang="scss">
  .popper-pen{
    background-color: rgba(0, 0, 0, .5);
    padding: 5px;
    border: none;
    cursor: pointer;
    pointer-events: auto;
    z-index: 9;
    .pen-box{
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: space-around;
      .pen-item{
        background-color: #fff;
      }
      .pen-size-10{
        width: 10px;
        height: 10px;
      }
      .pen-size-14{
        width: 14px;
        height: 14px;
      }
      .pen-size-18{
        width: 18px;
        height: 18px;
      }
      .active{
        background-color: #f59a23;
      }
    }
    .popper__arrow, .popper__arrow::after{
      border-top-color: rgba(0, 0, 0, .5) !important;
    }
  }
</style>
